package com.broadsoft.platform.wfengine.runtime;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.broadsoft.platform.wfengine.DecisionType;
import com.broadsoft.platform.wfengine.EndNodeType;
import com.broadsoft.platform.wfengine.GeneralNodeType;
import com.broadsoft.platform.wfengine.JoinType;
import com.broadsoft.platform.wfengine.NodeType;
import com.broadsoft.platform.wfengine.SplitType;
import com.broadsoft.platform.wfengine.StartNodeType;
import com.broadsoft.platform.wfengine.SubflowType;
import com.broadsoft.platform.wfengine.WorkflowType;
import com.broadsoft.platform.wfengine.helper.NodeTypeHelper;
import com.broadsoft.platform.wfengine.helper.WfConstants;
import com.broadsoft.platform.wfengine.parser.ParsedWorkflowDefinition;

/**
 * 
 * FIXME: <A few lines of description of what class is about and what it does.>
 * 
 * <HR/>
 * <B>Modification Block</B>
 * <P>
 * Version: FIXME: 1.0.1 <BR>
 * Author: FIXME: luzhen Date: 2011-9-15 TAG: <A short name to find out all changed made by this modification.>
 * Comments: Created. Bug fix NO.: <If required>
 * </P>
 * <P>
 * ... Other Modifications following above structure.
 * </P>
 * 
 */
public class WfEngine implements WfConstants {

    private final static Logger logger = LoggerFactory.getLogger(WfEngine.class);
    
    private NodeExecutionService nodeExecutionService;
    
    /**
     * 
     * FIXME: <A brief description of what the method does.> FIXME: Algorithm: <MUST exist if the method is more than 10
     * lines. Remove if it is simple.> <An Algorithm in pure English, describing the logic used in the implementation.
     * This should not contain to any Syntax of Java, in fact, it should be language independent, and should be targeted
     * to any new comer in the maintenance of the code.>
     * 
     * @param _node
     * @param flowContext
     * @throws Exception
     */
    public void executeNode(NodeType _node, WfContext flowContext) throws Exception {
        NodeType nextNode = _node;
        boolean endFlagOnDebugLong=false;
        do {
            if (logger.isDebugEnabled()) {
                NodeType currentNode = flowContext.getPreviousNode();
                logger.debug("["+Thread.currentThread().getName()+"]>>>>>Enter the node [{}] in the workflow[{}] .",nextNode.getName(), flowContext
                        .getWorkflowType().getName());
            }
            int type = NodeTypeHelper.getTypeOfNode(nextNode); 
            switch (type) {
            case START_NODE:
                nextNode=nodeExecutionService.executeStartNode((StartNodeType)nextNode,flowContext);
                break;
            case GENERAL_NODE:
                nextNode=nodeExecutionService.executeGeneralNode((GeneralNodeType)nextNode,flowContext);
                break;
            case DECISION_NODE:
                nextNode=nodeExecutionService.executeDecisionNode((DecisionType)nextNode,flowContext);
                break;
            case SUB_FLOW:
                nextNode=nodeExecutionService.executeSubflowNode((SubflowType)nextNode,flowContext);
                break;
            case JOIN_NODE:
                nextNode=nodeExecutionService.executeJoinNode((JoinType)nextNode, flowContext);
                break;
            case SPLIT_NODE:
                nextNode=nodeExecutionService.executeSplitNode((SplitType)nextNode, flowContext);
                break;
            case TIMER_NODE:
                nextNode=nodeExecutionService.executeTimerNode(nextNode, flowContext); 
                break; 
            case EXTERNAL_NODE:
                nextNode=nodeExecutionService.executeExternalNode(nextNode, flowContext); 
                break;     
            case END_NODE:
                nextNode=nodeExecutionService.executeEndNode((EndNodeType)nextNode,flowContext); 
                endFlagOnDebugLong=true;
                break;
              
            }
            if (logger.isDebugEnabled()) {
                NodeType currentNode = flowContext.getPreviousNode();
                logger.debug("["+Thread.currentThread().getName()+"]<<<<<Leave the node [{}] in the workflow[{}] .", currentNode.getName(), flowContext
                        .getWorkflowType().getName());
            }
        } while (nextNode != null);
        
        if (logger.isDebugEnabled()&&endFlagOnDebugLong) { 
            logger.debug("=============<<<<<<The flow[{}] is ended .",flowContext.getWorkflowType().getName());
        }
    }// end of executeNode
    
    
    /**
     * 
     * FIXME: <A brief description of what the method does.> 
     * FIXME: Algorithm: <MUST exist if the method is more than 10 lines. Remove if it is simple.>
     * <An Algorithm in pure English, describing the logic used in the implementation. 
     * This should not contain to any Syntax of Java, in fact, it should be language independent, 
     * and should be targeted to any new comer in the maintenance of the code.>
     *
     * @param flowName
     * @param flowContext
     * @throws Exception
     */
    public void start(String flowName,WfContext flowContext) throws Exception{
        if(logger.isDebugEnabled()){
            logger.debug("=============>>>>>>The flow[{}] is started .",flowName);
        }
        WorkflowType workflowType= parsedWorkflowInfo.getWorkflowTypeByName(flowName);
        StartNodeType startNodeType= workflowType.getStartNode();
        flowContext.setWorkflowType(workflowType);
        executeNode(startNodeType,flowContext);
    }//end of start
    
    
    
    /**
     * 
     * FIXME: <A brief description of what the method does.> 
     * FIXME: Algorithm: <MUST exist if the method is more than 10 lines. Remove if it is simple.>
     * <An Algorithm in pure English, describing the logic used in the implementation. 
     * This should not contain to any Syntax of Java, in fact, it should be language independent, 
     * and should be targeted to any new comer in the maintenance of the code.>
     *
     * @param flowName
     * @param flowContext
     * @throws Exception
     */
    public void signal(String flowName,String nodeName,WfContext flowContext) throws Exception{
        if(logger.isTraceEnabled()){
            logger.debug("The method[signal] begin.");
        } 
        NodeType currentNode=parsedWorkflowInfo.getNodeTypeByName(flowName, nodeName);
        executeNode(currentNode, flowContext) ;
        if(logger.isTraceEnabled()){
            logger.debug("The method[signal] end.");
        } 
    }//end of start
    
    
    
         

    public NodeExecutionService getNodeHandler() {
        return nodeExecutionService;
    }

    public void setNodeHandler(NodeExecutionService nodeHandler) {
        this.nodeExecutionService = nodeHandler;
    }
    private ParsedWorkflowDefinition parsedWorkflowInfo;

    public ParsedWorkflowDefinition getParsedWorkflowInfo() {
        return parsedWorkflowInfo;
    }

    public void setParsedWorkflowInfo(ParsedWorkflowDefinition parsedWorkflowInfo) {
        this.parsedWorkflowInfo = parsedWorkflowInfo;
    }

   

}
